/***************************************************************************
 *   Copyright (C) 2007 by Grzegorz Latarowski, Maciej Gajewski            *
 *   g.latarowski@gmail.com                                                *
 *   maciej.gajewski0@gmail.com                                            *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/


// local
#include "gpropertyeditor.h"
#include "gdoublespinboxdelegate.h"
#include "gspinboxdelegate.h"
#include "gcomboboxdelegate.h"
#include "gbooldelegate.h"
#include "gstringdelegate.h"




// ===========================================================================
//                              GPropertyEditor
// ===========================================================================



// ---------------------- GPropertyEditor::GPropertyEditor() -----------------
GPropertyEditor::GPropertyEditor( QWidget * pParent )
	: QTableView( pParent )
{
	// setup special configuration such as in QDesigner "Property Editor"
	
		// set selecting only rows 
	this->setSelectionBehavior( QAbstractItemView::SelectRows );
		// set selecting only single elements (rows) 
	this->setSelectionMode( QAbstractItemView::SingleSelection );
		// set alternate row coloring 
	this->setAlternatingRowColors ( true );
		// start editing by even single click
	this->setEditTriggers( QAbstractItemView::AllEditTriggers );
	
		// setup two columns model
	_pModel = new QStandardItemModel( 0, 2 );
	
		// set model to table view 
	this->setModel( _pModel );
	
	QTimer::singleShot( 100, this, SLOT( setupTable() ) );
}
// ---------------------------------------------------------------------------


// ------------------- GPropertyEditor::setupTable() -------------------------
void GPropertyEditor::setupTable()
{
		// hide vertical header
	if( this->verticalHeader() )
	{
		this->verticalHeader()->setVisible( false );
	}
	
		// fit row heights
	this->resizeColumnToContents( 0 );
	this->resizeRowsToContents();
		// set horizontal header labels 
	QStringList labels;
	labels << tr( "Property" ) << tr( "Value" );
	_pModel->setHorizontalHeaderLabels( labels );
	if( this->horizontalHeader() )
	{
		this->horizontalHeader()->setHighlightSections( false );
		this->horizontalHeader()->setResizeMode( 0, QHeaderView::Interactive );
		this->horizontalHeader()->setResizeMode( 1, QHeaderView::Stretch );
	}
}
// ---------------------------------------------------------------------------


// ------------------- GPropertyEditor::setParamsDescription() ---------------
void GPropertyEditor::setParamsDescription( 
		const GParamsDescription & paramsDescription
										  )
{
	if( !_pModel ) return;
	
		// disconnect item changed signal
	disconnect( _pModel, SIGNAL( itemChanged( QStandardItem * ) ),
			    this, SIGNAL( itemChanged() ) );
	
	_paramsDescription = paramsDescription;

		// clear model
	_pModel->removeRows( 0, _pModel->rowCount() );
		// clear params
	_params.clear();
	_paramsRowMap.clear();
	
		// search through map
	int row = 0;
	foreach( GParamDescription description, _paramsDescription )
	{
		QList<QStandardItem *> items;
			// insert current row and param formal name into _paramsRowMap
		_paramsRowMap.insert( row, description.name() );
			// insert params formal name and its QVariant value to params map 
		_params.insert( description.name(), description.defaultValue() );
			// insert params human-readable name
		QStandardItem * pItemLabel = new QStandardItem();
		pItemLabel->setFlags( Qt::ItemIsSelectable | Qt::ItemIsEnabled );
		pItemLabel->setData( description.readableName(), Qt::DisplayRole );
		items << pItemLabel;
			// insert params QVariant value into model
		QStandardItem * pItemValue = new QStandardItem();
		QVariant value( description.type() );
		value = description.defaultValue();

			// create delegate for item
		GItemDelegate * pDelegate = createItemDelegate( description );
		if( pDelegate )
		{
			connect( pDelegate, SIGNAL( itemChanged() ), this, SIGNAL( itemChanged() ) );
		}
		
		// NOTE: actula value is stored in Qt::UserRole role,
		// Qt::DisplayRole holds display only
		
		// set edit role for scalar values
		if ( description.items().isEmpty() )
		{
			pItemValue->setData( pDelegate->displayText( value ) , Qt::DisplayRole );
			pItemValue->setData( value, Qt::UserRole );
		}
		// set value for combo-box sets
		else
		{
			pItemValue->setData( description.valueName( value ), Qt::DisplayRole );
			pItemValue->setData( value, Qt::UserRole );
		}

			// add item to items list 
		items << pItemValue;

			// insert items to model
		_pModel->appendRow( items );

			// insert delegate to model 
		this->setItemDelegateForRow( row, pDelegate );
		
			// increment row counter
		row++;
	}
	_pModel->submit();

	setupTable();
	
		// reconnect itemChanged signal
	connect( _pModel, SIGNAL( itemChanged( QStandardItem * ) ),
			 this, SIGNAL( itemChanged() ) );
}
// ---------------------------------------------------------------------------


// ------------------- GPropertyEditor::setParams() --------------------------
void GPropertyEditor::setParams( const GParams & params )
{
	if( !_pModel ) return;

		// disconnect item changed signal
	disconnect( _pModel, SIGNAL( itemChanged( QStandardItem * ) ),
			    this, SIGNAL( itemChanged() ) );
	
		// copy all items from params to _params
	_params = params;

		// update values in the model,
		// use row number stored in _paramsRowMap
	QMapIterator< int, QString > j( _paramsRowMap );
	while( j.hasNext() )
	{
		j.next();
		QVariant value = _params.value( j.value() );
		QStandardItem * pItem = _pModel->item( j.key(), 1 );
		if( pItem )
		{
				// set display text
			QAbstractItemDelegate * pDelegate = this->itemDelegateForRow( j.key() );
			GItemDelegate * pCurrentDelegate;
			pCurrentDelegate = qobject_cast<GItemDelegate *>( pDelegate );
			if( pCurrentDelegate )
			{
				QString displayText = pCurrentDelegate->displayText( value );
				pItem->setData( displayText, Qt::DisplayRole );
			}
			pItem->setData( value, Qt::UserRole );
		}
	}

		// reconnect itemChanged signal
	connect( _pModel, SIGNAL( itemChanged( QStandardItem * ) ),
			 this, SIGNAL( itemChanged() ) );

	emit itemChanged();
}
// ---------------------------------------------------------------------------


// ------------------- GPropertyEditor::params() -----------------------------
GParams GPropertyEditor::params()
{
	if( !_pModel ) return GParams();
	
	// for each row number get from _paramsRowMap params
	// formal name and use it as a key for _params map

	int rowCount = _pModel->rowCount(); 
	for( int i = 0; i < rowCount; i++ )
	{
		QString formalName = _paramsRowMap.value( i );
		QVariant value = _pModel->item( i, 1 )->data( Qt::UserRole );
		_params.insert( formalName, value );
					  
	}

	return _params;
}
// ---------------------------------------------------------------------------


// ----------------- GPropertyEditor::createItemDelegate() -------------------
GItemDelegate * GPropertyEditor::createItemDelegate(
		const GParamDescription & description
												   )
{
	// is scalar? (no-combo box)
	if ( description.items().isEmpty() )
	{
		
		// scalar delegates
		switch( description.type() )
		{
			case QVariant::Int: // create spinBoxDelegate for INT 
			{
				GSpinBoxDelegate * pSpinBoxDelegate = new GSpinBoxDelegate( this );
				if( pSpinBoxDelegate )
				{
					pSpinBoxDelegate->setMinimum( description.minimum().toInt() );
					pSpinBoxDelegate->setMaximum( description.maximum().toInt() );
					pSpinBoxDelegate->setSingleStep( description.singleStep().toInt() );
				}
				return pSpinBoxDelegate;
			}
			
			case QVariant::Double: // create spinBoxDelegate for DOUBLE 
			{
				GDoubleSpinBoxDelegate * pDoubleSpinBoxDelegate = new GDoubleSpinBoxDelegate( this );
				if( pDoubleSpinBoxDelegate )
				{
					pDoubleSpinBoxDelegate->setMinimum( description.minimum().toDouble() );
					pDoubleSpinBoxDelegate->setMaximum( description.maximum().toDouble() );
					pDoubleSpinBoxDelegate->setSingleStep( description.singleStep().toDouble() );
					pDoubleSpinBoxDelegate->setDecimals( description.decimals() );
				}
				return pDoubleSpinBoxDelegate;
			}

			case QVariant::Bool: // create boolDelegate for BOOL
			{
				return new GBoolDelegate( this );
			}

			case QVariant::String: // create stringDelegate for STRING
			{
				return new GStringDelegate( this );
			}
			
			default:
			{
				return new GItemDelegate( this );
			}
		}
	}
	else // combo - box delegate
	{
		GComboBoxDelegate * pComboBoxDelegate = new GComboBoxDelegate( this );
		if( pComboBoxDelegate )
		{
			pComboBoxDelegate->setValues( description.items() );
		}
		return pComboBoxDelegate;
	}
	
	return 0;
}
// ---------------------------------------------------------------------------









// eof
